{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "both",
        "colab": {},
        "colab_type": "code",
        "id": "7GxUxaCDn-BU"
      },
      "outputs": [],
      "source": [
        "#@title Install dependencies\n",
        "\n",
        "!pip install --upgrade tensorflow\n",
        "!pip install tff-nightly"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "metadata": {
        "cellView": "both",
        "colab": {},
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 7809,
          "status": "ok",
          "timestamp": 1593632280834,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "lUoalEum5l00"
      },
      "outputs": [],
      "source": [
        "#@title Imports\n",
        "import tensorflow as tf\n",
        "import tf_quant_finance as tff\n",
        "import numpy as np\n",
        "import datetime\n",
        "import pandas as pd"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "LCTMGpuSeP_n"
      },
      "source": [
        "# Date Tensor Essentials\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "ySeShHlSsGep"
      },
      "source": [
        "## Constructing DateTensors\n",
        "\n",
        "\n",
        "\n",
        "There are 5 possible ways of constructing Date Tensors using tff.datetime.\n",
        "\n",
        "1.   Sequence of `datetime.datetime`, `datetime.date`, or any other object with attributes or properties called `year`, `month` and `day`.\n",
        "2.   A numpy array of `datetime64` type. \n",
        "3.   Sequence of (year, month, day) tuples. Months are 1-based (with January as 1) and tff.datetime.Month enum may be used instead of ints. Days are also 1-based. \n",
        "4.   A tuple of three int32 `Tensors` containing year, month and date as positive integers in that order.\n",
        "5.   A single int32 `Tensor` containing ordinals (i.e. number of days since 31 Dec 0 with 1 being 1 Jan 1.)\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "colab": {
          "height": 51
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 488,
          "status": "ok",
          "timestamp": 1593632389436,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "6FFc4SzjDN_S",
        "outputId": "b4355588-6e8c-49ad-d6c8-f400c1f470a3"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(2,), contents=array([[2015,    4,   15],\n",
              "       [2017,   12,   30]], dtype=int32)"
            ]
          },
          "execution_count": 3,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "#@title (1) Constructing Dates: sequence of `datetime.datetime`\n",
        "# Use Python's datetime library to construct a date as datetime.date(year, month, day).\n",
        "dates = [datetime.date(2015, 4, 15), datetime.date(2017, 12, 30)]\n",
        "# Then, convert this into a date tensor.\n",
        "date_tensor = tff.datetime.dates_from_datetimes(dates)\n",
        "date_tensor"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 463,
          "status": "ok",
          "timestamp": 1593633099249,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "0r2g5j41DI16",
        "outputId": "297c01be-706d-4de5-eb89-cef521c327b6"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(2, 2), contents=array([[[2019,    3,   25],\n",
              "        [2020,    6,    2]],\n",
              "\n",
              "       [[2020,    9,   15],\n",
              "        [2020,   12,   27]]], dtype=int32)"
            ]
          },
          "execution_count": 9,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "#@title (2) Constructing Dates: a numpy array\n",
        "# You can also use a numpy array of dtype datetime64 (in this case, generated via Python's datetime library).\n",
        "dates_np = np.array(\n",
        "  [[datetime.date(2019, 3, 25), datetime.date(2020, 6, 2)],\n",
        "   [datetime.date(2020, 9, 15), datetime.date(2020, 12, 27)]],\n",
        "   dtype=np.datetime64)\n",
        "# Again, convert this into a date tensor.\n",
        "date_tensor = tff.datetime.dates_from_np_datetimes(dates_np)\n",
        "date_tensor"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {
        "colab": {
          "height": 51
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 384,
          "status": "ok",
          "timestamp": 1593633100453,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "pw4M_-33FVQr",
        "outputId": "990408eb-13ec-48d8-9bd9-38f74de7a3c7"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(2,), contents=array([[2020,    2,   25],\n",
              "       [2020,    3,    2]], dtype=int32)"
            ]
          },
          "execution_count": 10,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "#@title (3) Constructing Dates: sequence of tuples\n",
        "# You can start instead with a sequence of tuples.\n",
        "date_tensor = tff.datetime.dates_from_tuples([(2020, 2, 25), (2020, 3, 2)])\n",
        "date_tensor"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {
        "colab": {
          "height": 51
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 383,
          "status": "ok",
          "timestamp": 1593633101596,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "rTNr3TAZDejd",
        "outputId": "972a708e-816f-47f7-ce4e-7789ae856ce9"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(2,), contents=array([[2015,    4,    1],\n",
              "       [2017,   12,   30]], dtype=int32)"
            ]
          },
          "execution_count": 11,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "#@title (4) Constructing Dates: a tuple of three tensors\n",
        "# Another way of using tuples is to first create a tuple of three tensors for the respective Day, Month and Year. You can do this by using TensorFlow's 'constant' function as follows:\n",
        "year = tf.constant([2015, 2017], dtype=tf.int32)\n",
        "month = tf.constant([4, 12], dtype=tf.int32)\n",
        "day = tf.constant([1, 30], dtype=tf.int32)\n",
        "date_tensor = tff.datetime.dates_from_year_month_day(year, month, day)\n",
        "date_tensor"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {
        "colab": {
          "height": 221
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 365,
          "status": "ok",
          "timestamp": 1593633102661,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "CqI-JagLPHQ7",
        "outputId": "bcc95262-20cb-4830-e751-1dd12b730f34"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Invalid day-month pairing.\n",
            "Condition x \u003c= y did not hold.\n",
            "Indices of first 1 different values:\n",
            "[[0]]\n",
            "Corresponding x values:\n",
            "[31]\n",
            "Corresponding y values:\n",
            "[30]\n",
            "First 2 elements of x:\n",
            "[31 30]\n",
            "First 2 elements of y:\n",
            "[30 31]\n"
          ]
        }
      ],
      "source": [
        "# Note that if the days don't represent valid dates with their respective months or vice versa, you will get an `InvalidArgumentError`, e.g.:\n",
        "try:\n",
        "  year = tf.constant([2015, 2017], dtype=tf.int32)\n",
        "  month = tf.constant([4, 12], dtype=tf.int32)\n",
        "  day = tf.constant([31, 30], dtype=tf.int32)\n",
        "  date_tensor = tff.datetime.dates_from_year_month_day(year, month, day)\n",
        "except tf.errors.InvalidArgumentError as e:\n",
        "  print (e)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {
        "colab": {
          "height": 34
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 535,
          "status": "ok",
          "timestamp": 1593633104460,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "XSNiLVISDvex",
        "outputId": "1749f6e2-e92c-4804-ef7b-c6ad81eab8b2"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(1,), contents=array([[1, 1, 1]], dtype=int32)"
            ]
          },
          "execution_count": 13,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "#@title (5) Constructing Dates: a single tensor containing ordinals\n",
        "# And finally, you can create date tensors using ordinals. The ordinal value is\n",
        "# defined as the number of days since 1 Jan 0001. \n",
        "# So, for example, 1 Jan 0001 has the ordinal value of 1.\n",
        "ordinals = tf.constant([1], dtype=tf.int32)\n",
        "date_tensor = tff.datetime.dates_from_ordinals(ordinals)\n",
        "date_tensor"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 456,
          "status": "ok",
          "timestamp": 1593633105612,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "HiEW0sgiQWPI",
        "outputId": "a8460954-af84-4bb2-9881-eccee73f7f9a"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(5,), contents=array([[2015,    4,   15],\n",
              "       [2017,   12,   30],\n",
              "       [1871,    8,    4],\n",
              "       [2119,    9,    3],\n",
              "       [1913,    5,   10]], dtype=int32)"
            ]
          },
          "execution_count": 14,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# We can create more meaningful and numerous dates as follows:\n",
        "ordinals = tf.constant([\n",
        "    735703, 736693, 683219, 773829, 698473], dtype=tf.int32)\n",
        "date_tensor = tff.datetime.dates_from_ordinals(ordinals)\n",
        "date_tensor"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {
        "colab": {
          "height": 34
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 385,
          "status": "ok",
          "timestamp": 1593633106786,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "8b9suIazS5AP",
        "outputId": "6ff8f895-35a7-420f-aa23-50d2216d963b"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "736693"
            ]
          },
          "execution_count": 15,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# You can identify the ordinal value of a date by computing the number of days since 1 Jan 0001.\n",
        "delta = datetime.date(2017,12,30) - datetime.date(1,1,1)\n",
        "delta.days + 1"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_zgPFwQIUUbG"
      },
      "source": [
        "## Generating random dates\n",
        "\n",
        "To generate random dates from date tensors between specific start_dates (inclusive) and end_dates (exclusive), we can use tff.datetime.random_dates.  The end_dates must be a tensor of a shape compatible with start_dates. In this case we've started with a pair and requested a size of 10, meaning that our tensor will be the shape (10, 2)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {
        "colab": {
          "height": 510
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 475,
          "status": "ok",
          "timestamp": 1593633109386,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "83ybZV4VWxKk",
        "outputId": "c622a4a2-6613-4605-a26a-b8072c8e36df"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(10, 2), contents=array([[[2020,    7,   12],\n",
              "        [2020,   12,    5]],\n",
              "\n",
              "       [[2020,    7,    1],\n",
              "        [2021,    4,   15]],\n",
              "\n",
              "       [[2020,    8,   19],\n",
              "        [2021,    2,   28]],\n",
              "\n",
              "       [[2020,    9,   29],\n",
              "        [2021,    4,   14]],\n",
              "\n",
              "       [[2020,    7,   16],\n",
              "        [2021,    1,   20]],\n",
              "\n",
              "       [[2021,    1,   11],\n",
              "        [2021,    3,   26]],\n",
              "\n",
              "       [[2020,   11,   27],\n",
              "        [2020,    8,    8]],\n",
              "\n",
              "       [[2020,   10,   27],\n",
              "        [2020,   10,    7]],\n",
              "\n",
              "       [[2020,    6,   29],\n",
              "        [2020,    9,    8]],\n",
              "\n",
              "       [[2020,    6,    7],\n",
              "        [2021,    5,   16]]], dtype=int32)"
            ]
          },
          "execution_count": 16,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Generate random dates\n",
        "start_dates = tff.datetime.dates_from_tuples([\n",
        "    (2020, 5, 16),\n",
        "    (2020, 6, 13)\n",
        "  ])\n",
        "end_dates = tff.datetime.dates_from_tuples([(2021, 5, 21)])\n",
        "size = 10  # Generate 10 dates for each pair of (start, end date).\n",
        "random_dates = tff.datetime.random_dates(start_date=start_dates, end_date=end_dates, size=size)\n",
        "random_dates"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {
        "colab": {
          "height": 34
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 483,
          "status": "ok",
          "timestamp": 1593633110629,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "gTbFjnBXVTm9",
        "outputId": "8246f35c-5c28-41ce-ba77-661c5660731b"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Invalid Argument Error, Incompatible shapes\n"
          ]
        }
      ],
      "source": [
        "# In the following case, the start_dates shape (4) and end_dates shape (2) don't \n",
        "# broadcast, producing an error.\n",
        "try:\n",
        "  start_dates = tff.datetime.dates_from_tuples([\n",
        "    (2020, 5, 16),\n",
        "    (2020, 6, 13),\n",
        "    (2020, 10, 31),\n",
        "    (2020, 12, 1)\n",
        "  ])\n",
        "  end_dates = tff.datetime.dates_from_tuples([(2021, 5, 21), (2021, 10, 20)])\n",
        "  size = 4  # Generate 4 dates for each (start, end date).\n",
        "  random_dates = tff.datetime.random_dates(start_date=start_dates, end_date=end_dates, size=size)\n",
        "  random_dates\n",
        "except tf.errors.InvalidArgumentError:\n",
        "  print('Invalid Argument Error, Incompatible shapes')  "
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "L913G28mtkRq"
      },
      "source": [
        "### Broadcasting"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "metadata": {
        "colab": {
          "height": 340
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 385,
          "status": "ok",
          "timestamp": 1593633113114,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "UL7LU2mFXYzO",
        "outputId": "e2c73920-8d4b-4856-e98e-7142007cad40"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(4, 4), contents=array([[[2020,   10,   31],\n",
              "        [2020,    6,   16],\n",
              "        [2021,    2,   25],\n",
              "        [2021,    8,   10]],\n",
              "\n",
              "       [[2021,    3,   26],\n",
              "        [2020,    8,   14],\n",
              "        [2021,    7,   23],\n",
              "        [2021,    9,   16]],\n",
              "\n",
              "       [[2021,    4,   16],\n",
              "        [2021,    6,   18],\n",
              "        [2021,    5,   17],\n",
              "        [2020,   12,   11]],\n",
              "\n",
              "       [[2020,   11,   14],\n",
              "        [2021,    5,   31],\n",
              "        [2021,    4,    7],\n",
              "        [2021,    8,   29]]], dtype=int32)"
            ]
          },
          "execution_count": 18,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Instead, match the end_dates by using a scalar (single date), or a matching shape of (4)\n",
        "start_dates = tff.datetime.dates_from_tuples([\n",
        "    (2020, 5, 16),\n",
        "    (2020, 6, 13),\n",
        "    (2020, 10, 31),\n",
        "    (2020, 12, 1)\n",
        "  ])\n",
        "end_dates = tff.datetime.dates_from_tuples([\n",
        "    (2021, 5, 21), \n",
        "    (2021, 10, 20), \n",
        "    (2021, 12, 5), \n",
        "    (2021, 11, 20)\n",
        "  ])\n",
        "size = 4  # Generate 4 dates for each (start, end date).\n",
        "random_dates = tff.datetime.random_dates(\n",
        "    start_date=start_dates, end_date=end_dates, size=size\n",
        "    )\n",
        "random_dates"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "juqAkiMmTTFU"
      },
      "source": [
        "# Dates Exploration\n",
        "\n",
        "Now that we've constructed our dates, let's see what we can do with them."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 355,
          "status": "ok",
          "timestamp": 1593633115157,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "Ov-SE0D9WeOP",
        "outputId": "52428ed0-953c-45d6-a8b0-c1f59daecbaf"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(4, 4), dtype=int32, numpy=\n",
              "array([[31, 16, 25, 10],\n",
              "       [26, 14, 23, 16],\n",
              "       [16, 18, 17, 11],\n",
              "       [14, 31,  7, 29]], dtype=int32)\u003e"
            ]
          },
          "execution_count": 19,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Return the day, the month or the year from date tensors.\n",
        "random_dates.day()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 420,
          "status": "ok",
          "timestamp": 1593633116269,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "lg66bmNRXAWr",
        "outputId": "eb20c630-e0a0-4c9d-e0f3-eb8c37f67664"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(4, 4), dtype=int32, numpy=\n",
              "array([[10,  6,  2,  8],\n",
              "       [ 3,  8,  7,  9],\n",
              "       [ 4,  6,  5, 12],\n",
              "       [11,  5,  4,  8]], dtype=int32)\u003e"
            ]
          },
          "execution_count": 20,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "random_dates.month()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 363,
          "status": "ok",
          "timestamp": 1593633117358,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "MfM7lzUrXCJq",
        "outputId": "50bb25f8-13ea-4bb3-9bcf-07d5f0ec39dc"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(4, 4), dtype=int32, numpy=\n",
              "array([[2020, 2020, 2021, 2021],\n",
              "       [2021, 2020, 2021, 2021],\n",
              "       [2021, 2021, 2021, 2020],\n",
              "       [2020, 2021, 2021, 2021]], dtype=int32)\u003e"
            ]
          },
          "execution_count": 21,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "random_dates.year()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 394,
          "status": "ok",
          "timestamp": 1593633118524,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "eeKG8uG9XHL3",
        "outputId": "159ad97a-4143-4f64-f5e2-a9540c3080e6"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(4, 4), dtype=int32, numpy=\n",
              "array([[737729, 737592, 737846, 738012],\n",
              "       [737875, 737651, 737994, 738049],\n",
              "       [737896, 737959, 737927, 737770],\n",
              "       [737743, 737941, 737887, 738031]], dtype=int32)\u003e"
            ]
          },
          "execution_count": 22,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Or the ordinals of date tensors.\n",
        "random_dates.ordinal()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {
        "colab": {
          "height": 340
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 416,
          "status": "ok",
          "timestamp": 1593633119623,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "kq5Su9aGGkxE",
        "outputId": "c24088a6-f03e-4729-df32-ee5e1e2f7614"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(4, 4), contents=array([[[2020,   11,   10],\n",
              "        [2020,    6,   26],\n",
              "        [2021,    3,    7],\n",
              "        [2021,    8,   20]],\n",
              "\n",
              "       [[2021,    4,    5],\n",
              "        [2020,    8,   24],\n",
              "        [2021,    8,    2],\n",
              "        [2021,    9,   26]],\n",
              "\n",
              "       [[2021,    4,   26],\n",
              "        [2021,    6,   28],\n",
              "        [2021,    5,   27],\n",
              "        [2020,   12,   21]],\n",
              "\n",
              "       [[2020,   11,   24],\n",
              "        [2021,    6,   10],\n",
              "        [2021,    4,   17],\n",
              "        [2021,    9,    8]]], dtype=int32)"
            ]
          },
          "execution_count": 23,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# We can then use the days() function to return any multiple of days. For example,\n",
        "# what is the date 10 days from our date tensors?\n",
        "new_dates = random_dates + tff.datetime.day()*10\n",
        "new_dates"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 380,
          "status": "ok",
          "timestamp": 1593633120765,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "JPpB7PLGXPWs",
        "outputId": "f0a4a2d3-12fc-4413-dc02-caee7543c6d0"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(4, 4), dtype=int32, numpy=\n",
              "array([[5, 1, 3, 1],\n",
              "       [4, 4, 4, 3],\n",
              "       [4, 4, 0, 4],\n",
              "       [5, 0, 2, 6]], dtype=int32)\u003e"
            ]
          },
          "execution_count": 24,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# You can also identify the corresponding day of the week of your date tensors, \n",
        "# whereby Monday is \"0\" and Sunday is \"6\", according to Python dates convention.\n",
        "random_dates.day_of_week()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 476,
          "status": "ok",
          "timestamp": 1593633121925,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "p03f53pZXfAd",
        "outputId": "fd8edddc-369c-418c-9355-a37e4d382aad"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "tf.Tensor(\n",
            "[[b'Saturday' b'Tuesday' b'Thursday' b'Tuesday']\n",
            " [b'Friday' b'Friday' b'Friday' b'Thursday']\n",
            " [b'Friday' b'Friday' b'Monday' b'Friday']\n",
            " [b'Saturday' b'Monday' b'Wednesday' b'Sunday']], shape=(4, 4), dtype=string)\n"
          ]
        }
      ],
      "source": [
        "# To make this more intuitive, we can create a TF table with the assigned values \n",
        "# to then look up and print the corresponding day of the week.\n",
        "table = tf.lookup.StaticHashTable(\n",
        "    initializer=tf.lookup.KeyValueTensorInitializer(\n",
        "        keys=tf.constant([0, 1, 2, 3, 4, 5, 6]),\n",
        "        values=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']\n",
        "    ),\n",
        "    default_value='Monday',\n",
        "    name='days_in_week'\n",
        ")\n",
        "\n",
        "input_tensor = random_dates.day_of_week()\n",
        "out = table.lookup(input_tensor)\n",
        "print(out)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 361,
          "status": "ok",
          "timestamp": 1593633122889,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "Pj5rY-x6leMA",
        "outputId": "40e0521f-3aff-4f92-e8b5-00537560f087"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(4, 4), dtype=int32, numpy=\n",
              "array([[305, 168,  56, 222],\n",
              "       [ 85, 227, 204, 259],\n",
              "       [106, 169, 137, 346],\n",
              "       [319, 151,  97, 241]], dtype=int32)\u003e"
            ]
          },
          "execution_count": 26,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# What about the day of the year?\n",
        "random_dates.day_of_year()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 370,
          "status": "ok",
          "timestamp": 1593633123962,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "MSo6qR3UlmVL",
        "outputId": "8efe1ca8-4b65-4380-9084-16a68e18ea6a"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(4, 4), dtype=int32, numpy=\n",
              "array([[490, 627, 373, 207],\n",
              "       [344, 568, 225, 170],\n",
              "       [323, 260, 292, 449],\n",
              "       [476, 278, 332, 188]], dtype=int32)\u003e"
            ]
          },
          "execution_count": 27,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# We can also calculate the number of days until a target date\n",
        "target = tff.datetime.dates_from_tuples([(2022, 3, 5)])\n",
        "random_dates.days_until(target)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 364,
          "status": "ok",
          "timestamp": 1593633125068,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "vqF6u8URl5vB",
        "outputId": "218d30b5-29d0-4184-d0e3-8b7e968bb630"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(4, 4), dtype=int32, numpy=\n",
              "array([[-240,  658,  770, 1033],\n",
              "       [-386,  599,  622,  996],\n",
              "       [-407,  291,  689, 1275],\n",
              "       [-254,  309,  729, 1014]], dtype=int32)\u003e"
            ]
          },
          "execution_count": 28,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Or multiple target dates, but the shapes of the dates \u0026 targets tensors must broadcast.\n",
        "targets = tff.datetime.dates_from_tuples([(2020, 3, 5), (2022, 4, 5), (2023, 4, 6), (2024, 6, 8)])\n",
        "random_dates.days_until(targets)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "metadata": {
        "colab": {
          "height": 340
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 357,
          "status": "ok",
          "timestamp": 1593633126083,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "BBni4eUirXlG",
        "outputId": "089d89cc-e1e1-4b49-bda1-b47b11476f02"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(4, 4), contents=array([[[2020,   10,   31],\n",
              "        [2020,    6,   30],\n",
              "        [2021,    2,   28],\n",
              "        [2021,    8,   31]],\n",
              "\n",
              "       [[2021,    3,   31],\n",
              "        [2020,    8,   31],\n",
              "        [2021,    7,   31],\n",
              "        [2021,    9,   30]],\n",
              "\n",
              "       [[2021,    4,   30],\n",
              "        [2021,    6,   30],\n",
              "        [2021,    5,   31],\n",
              "        [2020,   12,   31]],\n",
              "\n",
              "       [[2020,   11,   30],\n",
              "        [2021,    5,   31],\n",
              "        [2021,    4,   30],\n",
              "        [2021,    8,   31]]], dtype=int32)"
            ]
          },
          "execution_count": 29,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Let's now shift our dates to the end of their respective months.\n",
        "random_dates.to_end_of_month()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "d783OaVQmXEd"
      },
      "source": [
        "## Periods\n",
        "\n",
        "Now, let's think about periods. A PeriodType can be any of the following: day, days, week, weeks, month, months, year or years. Often, this is used in conjunction with 'quantity' to calculate the quantity of periods within another period (i.e. how many months in a year)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 30,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 439,
          "status": "ok",
          "timestamp": 1593633128843,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "6xnsA7bSnzFY",
        "outputId": "9b7bf34f-b2b1-432a-d5ab-ba551e886b19"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(4, 4), dtype=int32, numpy=\n",
              "array([[30, 30, 28, 31],\n",
              "       [31, 31, 31, 30],\n",
              "       [30, 30, 31, 31],\n",
              "       [30, 30, 30, 31]], dtype=int32)\u003e"
            ]
          },
          "execution_count": 30,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# You can compute the number of days in specific periods, in this case the period\n",
        "# is months: How many days are in each month in our date tensors?\n",
        "random_dates.period_length_in_days(tff.datetime.month())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 413,
          "status": "ok",
          "timestamp": 1593633130059,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "H_vldVvJev01",
        "outputId": "21c53630-d56e-4ce0-f2a6-0edaa9efca20"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(4, 4), dtype=int32, numpy=\n",
              "array([[365, 365, 365, 365],\n",
              "       [365, 365, 365, 365],\n",
              "       [365, 365, 365, 365],\n",
              "       [365, 365, 365, 365]], dtype=int32)\u003e"
            ]
          },
          "execution_count": 31,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# What about using years as the period?\n",
        "random_dates.period_length_in_days(tff.datetime.year())"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 428,
          "status": "ok",
          "timestamp": 1593633131184,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "eS-EPiV1qZXr",
        "outputId": "c7b9274e-894f-43e3-a3bb-89924c27e5c8"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(4, 4), dtype=bool, numpy=\n",
              "array([[False, False, False, False],\n",
              "       [False, False, False, False],\n",
              "       [False, False, False, False],\n",
              "       [False, False, False, False]])\u003e"
            ]
          },
          "execution_count": 32,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Looks like there aren't any leap years in our dates, let's confirm. This\n",
        "# function is in the 'utils' part of our library.\n",
        "years = random_dates.period_length_in_days(tff.datetime.year())\n",
        "tff.datetime.utils.is_leap_year(years)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "metadata": {
        "colab": {
          "height": 34
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 479,
          "status": "ok",
          "timestamp": 1593633435577,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "2pnCAELXYLaG",
        "outputId": "91e18052-1f3d-41ff-f399-50dd029b7b95"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(2,), dtype=int32, numpy=array([121, 153], dtype=int32)\u003e"
            ]
          },
          "execution_count": 38,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# We can also specify the period. For example, how many days are there up to the \n",
        "# 4th and 5th month in 2020?\n",
        "dates = tff.datetime.dates_from_tuples([(2020, 2, 25), (2020, 3, 2)])\n",
        "periods = tff.datetime.months([4, 5])\n",
        "dates.period_length_in_days(periods)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "zjBfpr8Lz4r5"
      },
      "source": [
        "# **Holiday Calendar**\n",
        "\n",
        "Up to this point we've been using a standard year for our dates. We can also create Holiday Calendars in order to calculate dates taking business days and holidays into account.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "GutS7OroskOi"
      },
      "source": [
        "## Creating Holiday Calendars - with Pandas\n",
        "\n",
        "The first step will be to create our own Holiday Calendar. This can be done completely manually, however, it would then be necessary to provide holidays for each year and also adjust the holidays that fall on weekends if required. To avoid that, we can use AbstractHolidayCalendar from Pandas."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "metadata": {
        "colab": {
          "height": 119
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 438,
          "status": "ok",
          "timestamp": 1593633462654,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "LW0_b1vvsO2b",
        "outputId": "23356855-7e41-4c0b-9f77-c1abad2961cc"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "array(['2020-01-01', '2020-12-25', '2021-01-01', '2021-12-24',\n",
              "       '2021-12-31', '2022-12-26', '2023-01-02', '2023-12-25',\n",
              "       '2024-01-01', '2024-12-25', '2025-01-01', '2025-12-25',\n",
              "       '2026-01-01', '2026-12-25', '2027-01-01', '2027-12-24',\n",
              "       '2027-12-31', '2028-12-25', '2029-01-01', '2029-12-25',\n",
              "       '2030-01-01', '2030-12-25'], dtype='datetime64[D]')"
            ]
          },
          "execution_count": 39,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Start with the necessary imports.\n",
        "from pandas.tseries.holiday import AbstractHolidayCalendar\n",
        "from pandas.tseries.holiday import Holiday\n",
        "from pandas.tseries.holiday import nearest_workday\n",
        "\n",
        "# Define the rules (i.e. holidays) for the Calendar.\n",
        "class MyCalendar(AbstractHolidayCalendar):\n",
        "    rules = [\n",
        "        Holiday('NewYear', month=1, day=1, observance=nearest_workday),\n",
        "        Holiday('Christmas', month=12, day=25,\n",
        "                  observance=nearest_workday)\n",
        "    ]\n",
        "calendar = MyCalendar()\n",
        "holidays_index = calendar.holidays(\n",
        "    start=datetime.date(2020, 1, 1),\n",
        "    end=datetime.date(2030, 12, 31))\n",
        "holidays = np.array(holidays_index.to_pydatetime(), dtype=\"\u003cM8[D]\")\n",
        "holidays"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 40,
      "metadata": {
        "colab": {
          "height": 102
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 402,
          "status": "ok",
          "timestamp": 1593633463793,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "bEXj8TWZwVYD",
        "outputId": "0817976d-69a6-49e5-c634-09ab90ff907e"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "tf.Tensor(\n",
            "[b'Wednesday' b'Friday' b'Friday' b'Friday' b'Friday' b'Monday' b'Monday'\n",
            " b'Monday' b'Monday' b'Wednesday' b'Wednesday' b'Thursday' b'Thursday'\n",
            " b'Friday' b'Friday' b'Friday' b'Friday' b'Monday' b'Monday' b'Tuesday'\n",
            " b'Tuesday' b'Wednesday'], shape=(22,), dtype=string)\n"
          ]
        }
      ],
      "source": [
        "# As you can see, all of the holidays have been adjusted to a week day, as would be the case for that year's Holiday Calendar.\n",
        "date_tensor = tff.datetime.dates_from_np_datetimes(holidays)\n",
        "input_tensor = date_tensor.day_of_week()\n",
        "out = table.lookup(input_tensor)\n",
        "print(out)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "NO9MnBH-xsfT"
      },
      "source": [
        "## Creating Holiday Calendars - manually\n",
        "\n",
        "Let's now create our own Holiday Calendar using the TFF Library. To do this, we need to specify:\n",
        "- A Weekend Mask: Boolean `Tensor` of 7 elements one for each day of the week starting with Monday at index 0. A `True` value indicates the day is considered a weekend day and a `False` value implies a week day.\n",
        "Default value: None which means no weekends are applied. The following enums for common weekend patterns are also accepted: `SATURDAY_SUNDAY`, `FRIDAY_SATURDAY`, `SUNDAY_ONLY`, `NONE`.\n",
        "- Holidays: In this case it will be necessary to provide holidays for each year, and also adjust the holidays to that fall on weekdays if necessary.\n",
        "- Start Year: the earliest year this calendar includes\n",
        "- End Year: the latest year this calendar includes"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 41,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 385,
          "status": "ok",
          "timestamp": 1593633466275,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "SeQwp_pxh14-"
      },
      "outputs": [],
      "source": [
        "# Create a calendar\n",
        "cal = tff.datetime.create_holiday_calendar(weekend_mask=tff.datetime.WeekendMask.SATURDAY_SUNDAY,\n",
        "                                           holidays=[(2020, 2, 25), (2020, 2, 26), (2019, 12, 25), (2019, 12, 26)], start_year=2019, end_year=2020)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 42,
      "metadata": {
        "colab": {
          "height": 34
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 355,
          "status": "ok",
          "timestamp": 1593633468641,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "6LnNWT8ifmhq",
        "outputId": "d38db948-dcf3-438b-d295-ddd2a93bb533"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(2,), dtype=bool, numpy=array([False,  True])\u003e"
            ]
          },
          "execution_count": 42,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Now, let's test it. Is 'dates' a business day?\n",
        "dates = tff.datetime.dates_from_tuples([(2020, 2, 25), (2020, 3, 20)])\n",
        "cal.is_business_day(dates)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 43,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 379,
          "status": "ok",
          "timestamp": 1593633469774,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "jRSUNsc70SrS"
      },
      "outputs": [],
      "source": [
        "# Rather than using a WeekendMask Enum, let's create our own for 4-day weekends.\n",
        "new_cal = tff.datetime.create_holiday_calendar(weekend_mask = (0, 0, 0, 1, 1, 1, 1),\n",
        "                                           holidays=[(2020, 2, 25), (2020, 2, 26), (2019, 12, 25), (2019, 12, 26)], start_year=2019, end_year=2020)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 44,
      "metadata": {
        "colab": {
          "height": 34
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 335,
          "status": "ok",
          "timestamp": 1593633470952,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "w-laBs5u2jF_",
        "outputId": "cd953445-218f-4dbd-ff31-f4353715654d"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(2,), dtype=bool, numpy=array([False, False])\u003e"
            ]
          },
          "execution_count": 44,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Let's see if the same holds true - is 'dates' a business day?\n",
        "dates = tff.datetime.dates_from_tuples([(2020, 2, 25), (2020, 3, 20)])\n",
        "new_cal.is_business_day(dates)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 45,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 437,
          "status": "ok",
          "timestamp": 1593633472306,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "ftQ1V54c3tfJ"
      },
      "outputs": [],
      "source": [
        "# Great, now we have both days off!"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "JVzYqiBPPOwK"
      },
      "source": [
        "##Roll Conventions\n",
        "\n",
        "Now that we have our holiday calendar and know how to work with dates, we can apply roll conventions to determine where the business days fall. The main argument is a `BusinessDayConvention` enum which determines how to roll a date that falls on a holiday (including weekends):\n",
        "\n",
        "*   `NONE`: No adjustment.\n",
        "*   `FOLLOWING`: Choose the first business day after the given holiday.\n",
        "*   `MODIFIED_FOLLOWING`: Choose the first business day after the given holiday\n",
        "  unless that day falls in the next calendar month, in which case choose the\n",
        "  first business day before the holiday.\n",
        "*   `PRECEDING`: Choose the first business day before the given holiday.\n",
        "*   `MODIFIED_PRECEDING`: Choose the first business day before the given holiday unless that day falls in the previous calendar month, in which case choose the first business day after the holiday.\n",
        "\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 46,
      "metadata": {
        "colab": {
          "height": 51
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 354,
          "status": "ok",
          "timestamp": 1593633474421,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "QlgoiSccRLJ-",
        "outputId": "b906adb8-ee35-49a7-80f7-7a4a52f40e36"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(2,), contents=array([[2020,    3,    2],\n",
              "       [2020,    3,   23]], dtype=int32)"
            ]
          },
          "execution_count": 46,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Based on our four-day weekend holiday calendar, let's see what the next\n",
        "# business days are according to the `FOLLOWING` Convention:\n",
        "new_cal.roll_to_business_day(dates, roll_convention=tff.datetime.BusinessDayConvention.FOLLOWING)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 47,
      "metadata": {
        "colab": {
          "height": 51
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 413,
          "status": "ok",
          "timestamp": 1593633475659,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "B_x-LkDiTIL-",
        "outputId": "f00f8ae1-e65a-4b33-9d59-a9fefdc8b6b7"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(2,), contents=array([[2020,    2,   24],\n",
              "       [2020,    3,   23]], dtype=int32)"
            ]
          },
          "execution_count": 47,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Since the first date transitions us to the following months, let's see how\n",
        "# `MODIFIED_FOLLOWING` works.\n",
        "new_cal.roll_to_business_day(dates, roll_convention=tff.datetime.BusinessDayConvention.MODIFIED_FOLLOWING)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 48,
      "metadata": {
        "colab": {
          "height": 51
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 373,
          "status": "ok",
          "timestamp": 1593633476749,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "U7Faa1xIVV2V",
        "outputId": "f49e9ef0-58c2-497d-8ece-2394e510bbf9"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(2,), contents=array([[2020,    3,   16],\n",
              "       [2020,    4,    6]], dtype=int32)"
            ]
          },
          "execution_count": 48,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# We can also add or subtract business days using a roll convention, where \n",
        "# the second argument is the number of days we want to add/subtract, as follows:\n",
        "new_cal.add_business_days(dates, 6, tff.datetime.BusinessDayConvention.FOLLOWING)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 49,
      "metadata": {
        "colab": {
          "height": 51
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 404,
          "status": "ok",
          "timestamp": 1593633478048,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "t4oqEcW4VYWi",
        "outputId": "237eef9b-b844-4413-c3e2-93772589efdc"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(2,), contents=array([[2020,    2,   11],\n",
              "       [2020,    3,    9]], dtype=int32)"
            ]
          },
          "execution_count": 49,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "new_cal.subtract_business_days(dates, 6, tff.datetime.BusinessDayConvention.FOLLOWING)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "lrlKem4-tQ8-"
      },
      "source": [
        "# **Day Count Conventions**\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "VgAx5bBiNmQ5"
      },
      "source": [
        "Day count conventions are a system for determining how a coupon accumulates over a coupon period. They can also be seen as a method for converting date\n",
        "differences to elapsed time. The functions in this module of our library are based on the commonly used day count conventions: \n",
        "\n",
        "\n",
        "*   Actual (ISDA)\n",
        "*   Actual 360\n",
        "*   Actual 365\n",
        "*   Actual 365 fixed\n",
        "*   Thirty 360 (ISDA)\n",
        "\n",
        "*examples coming soon*"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "cws8Jbkc0L4v"
      },
      "source": [
        "# **Scaling up**\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 50,
      "metadata": {
        "colab": {
          "height": 136
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 377,
          "status": "ok",
          "timestamp": 1593633483447,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "AHf2SUTTfwKb",
        "outputId": "5fcfc939-2cb8-45e1-aeb0-0a7790da435f"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(1, 1827), contents=array([[[2015,    1,    1],\n",
              "        [2015,    1,    2],\n",
              "        [2015,    1,    3],\n",
              "        ...,\n",
              "        [2019,   12,   30],\n",
              "        [2019,   12,   31],\n",
              "        [2020,    1,    1]]], dtype=int32)"
            ]
          },
          "execution_count": 50,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# Let's now consider 5 years worth of dates. We'll do this by using the \n",
        "# PeriodSchedule.dates function in the library, which is useful for creating \n",
        "# dates within a range.\n",
        "start_date=tff.datetime.dates_from_tuples([(2015, 1, 1)])\n",
        "end_date=tff.datetime.dates_from_tuples([(2020, 1, 1)])\n",
        "tenor = tff.datetime.day()\n",
        "date_range = tff.datetime.PeriodicSchedule(start_date=start_date, end_date=end_date, tenor=tenor)\n",
        "date_range.dates()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 71,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 356,
          "status": "ok",
          "timestamp": 1593633910168,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "gV-ZAcK-68jY"
      },
      "outputs": [],
      "source": [
        "# We can do this for an even bigger date range. Let's see how long that takes.\n",
        "start_date=tff.datetime.dates_from_tuples([(1001, 1, 1)])\n",
        "end_date=tff.datetime.dates_from_tuples([(2020, 1, 1)])\n",
        "tenor = tff.datetime.day()\n",
        "date_range = tff.datetime.PeriodicSchedule(start_date=start_date_alt, end_date=end_date, tenor=tenor)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 72,
      "metadata": {
        "colab": {
          "height": 34
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 2563,
          "status": "ok",
          "timestamp": 1593633913539,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "uU8ZC4BZwMg2",
        "outputId": "7b836aa8-3511-42b6-f108-f2e1e9aebf14"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "10 loops, best of 3: 53.3 ms per loop\n"
          ]
        }
      ],
      "source": [
        "%%timeit\n",
        "date_range = tff.datetime.PeriodicSchedule(start_date=start_date, end_date=end_date, tenor=tenor)\n",
        "date_range.dates()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 73,
      "metadata": {
        "colab": {
          "height": 136
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 396,
          "status": "ok",
          "timestamp": 1593633915385,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "Yin6gwre9uvz",
        "outputId": "47b49f8a-9016-47ac-d635-08aa1651767e"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "DateTensor: shape=(1, 372183), contents=array([[[1001,    1,    1],\n",
              "        [1001,    1,    2],\n",
              "        [1001,    1,    3],\n",
              "        ...,\n",
              "        [2019,   12,   30],\n",
              "        [2019,   12,   31],\n",
              "        [2020,    1,    1]]], dtype=int32)"
            ]
          },
          "execution_count": 73,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "dates = large_date_range.dates()\n",
        "dates"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 74,
      "metadata": {
        "colab": {
          "height": 34
        },
        "colab_type": "code",
        "executionInfo": {
          "elapsed": 348,
          "status": "ok",
          "timestamp": 1593633920583,
          "user": {
            "displayName": "",
            "photoUrl": "",
            "userId": ""
          },
          "user_tz": -60
        },
        "id": "XupiasmviPLf",
        "outputId": "bf002778-f5cc-4427-e738-7161f61034e2"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "\u003ctf.Tensor: shape=(), dtype=float32, numpy=90403.0\u003e"
            ]
          },
          "execution_count": 74,
          "metadata": {
            "tags": []
          },
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# How many leap years are within these dates?\n",
        "years = dates.year()\n",
        "leap_years_boolean = tff.datetime.utils.is_leap_year(years)\n",
        "tf.reduce_sum(tf.cast(leap_years_boolean, tf.float32)) # Count the number of 'True' values by casting the values to floats.#"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "41La2a7cXhpi"
      },
      "outputs": [],
      "source": [
        "# 90,403 leap years out of the 372,183 years we provided, sounds about right!"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "last_runtime": {
        "build_target": "//learning/brain/python/client:colab_notebook_py3",
        "kind": "private"
      },
      "name": "Dates_in_TFF.ipynb",
      "provenance": [
        {
          "file_id": "1vcxG8Z4Iwr0lmDkwX8T_m2GiLHiEbL-6",
          "timestamp": 1593613346342
        }
      ]
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
